import { baseURL } from "@/api";

let mimeMap = {
    jpg: 'image/jpeg',
    jpeg: 'image/jpeg',
    png: 'image/png',
    gif: 'image/gif',
    bmp: 'image/bmp',
    webp: 'image/webp',
}

export function FormObject(data: any) {
    const form = new FormData();
    const domain =  baseURL.replace("https://","").replace("http://","").replace("/","");
    Object.keys(data).forEach((key)=>{
        let value = data[key];
        let name = key.replace("List","");
        if (key.includes("List")) {
            if (value.length>0){
                value.forEach((item: any, index: number)=>{
                    const fileUrl = item.url;
                    const isRemote = item.isRemote || fileUrl.includes(domain);
                    if (!isRemote){
                        const ext = fileUrl.split(".")[1];
                        const filename = name+'.'+ext;
                        form.appendFile(`${name}[${index}]`, fileUrl, filename)
                    }else{
                        form.append(`${name}[${index}]`, item.path)
                    }
                })
            }else{
                form.append(`files[${name}]`, value)
            }
        }else if (key.includes("File")) {
            if (value.length>0){
                const fileUrl = value[0].url;
                const isRemote = value[0].isRemote || fileUrl.includes(domain);
                if (!isRemote){
                    const ext = fileUrl.split(".")[1];
                    const filename = name+'.'+ext;
                    form.appendFile(`files[${name}]`, fileUrl, filename)
                }
            }else{
                form.append(`files[${name}]`, value)
            }
        }else {
            form.append(name, value)
        }
    })
    return form;
}


interface CommonFormData {
    fileManager: any
    data: any
    files: any
    append(name: string, value: any): boolean
    appendFile(name: string, path: any, fileName?: any): boolean
    getData(): any
}

const manager = uni.getFileSystemManager()

export class FormData implements CommonFormData {
    fileManager: any
    data: any
    files: any
    constructor() {
        this.data = {}
        this.files = []
    }

    append(name: string, value: any) {
        this.data[name] = value
        return true
    }

    appendFile(name: string, path: any, fileName?: any) {
        let buffer = manager.readFileSync(path)
        if (Object.prototype.toString.call(buffer).indexOf('ArrayBuffer') < 0) {
            return false
        }
        if (!fileName) {
            fileName = getFileNameFromPath(path)
        }
        this.files.push({
            name: name,
            buffer: buffer,
            fileName: fileName,
        })
        return true
    }

    getData() {
        return convert(this.data, this.files)
    }
}

function getFileNameFromPath(path: any) {
    let idx = path.lastIndexOf('/')
    return path.substr(idx + 1)
}

function convert(data: any, files: any) {
    let boundaryKey = 'unimpFormBoundary' + randString() // 数据分割符，一般是随机的字符串
    let boundary = '--' + boundaryKey
    let endBoundary = boundary + '--'

    let postArray: any = []
    //拼接参数
    if (data && Object.prototype.toString.call(data) == '[object Object]') {
        for (let key in data) {
            postArray = postArray.concat(formDataArray(boundary, key, data[key]))
        }
    }
    //拼接文件
    if (files && Object.prototype.toString.call(files) == '[object Array]') {
        for (let i in files) {
            let file = files[i]
            postArray = postArray.concat(formDataArray(boundary, file.name, file.buffer, file.fileName))
        }
    }

    //结尾
    let endBoundaryArray = []
    // @ts-ignore
    endBoundaryArray.push(...endBoundary.toUtf8Bytes())
    postArray = postArray.concat(endBoundaryArray)

    return {
        contentType: 'multipart/form-data; boundary=' + boundaryKey,
        buffer: new Uint8Array(postArray).buffer,
    }
}

// 获取随机字符串
function randString() {
    var result = ''
    var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    for (var i = 17; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)]
    return result
}

function formDataArray(boundary: any, name: string, value: any, fileName?: string) {
    let dataString: any = ''
    let isFile = !!fileName

    dataString += boundary + '\r\n'
    dataString += 'Content-Disposition: form-data; name="' + name + '"'
    if (isFile) {
        dataString += '; filename="' + fileName + '"' + '\r\n'
        dataString += 'Content-Type: ' + getFileMime(fileName || '') + '\r\n\r\n'
    } else {
        dataString += '\r\n\r\n'
        dataString += value
    }

    var dataArray = []
    dataArray.push(...dataString.toUtf8Bytes())

    if (isFile) {
        let fileArray = new Uint8Array(value)
        dataArray = dataArray.concat(Array.prototype.slice.call(fileArray))
    }
    // @ts-ignore
    dataArray.push(...'\r'.toUtf8Bytes())
    // @ts-ignore
    dataArray.push(...'\n'.toUtf8Bytes())

    return dataArray
}

function getFileMime(fileName: string) {
    let idx = fileName.split('.')
    // let index: string = fileName.substr(idx)
    // let index: string = idx[1]
    // @ts-ignore
    let mime = mimeMap[idx[1]]
    return mime ? mime : 'application/octet-stream'
}

// @ts-ignore
function stringToUtf8(string: string) {
    let encoder = new TextEncoder()
    return encoder.encode(string)
}

// @ts-ignore
String.prototype.toUtf8Bytes = function () {
    var str: any = this
    var bytes = []
    for (var i = 0; i < str.length; i++) {
        bytes.push(...str.utf8CodeAt(i))
        if (str.codePointAt(i) > 0xffff) {
            i++
        }
    }
    return bytes
}

// @ts-ignore
String.prototype.utf8CodeAt = function (i: any) {
    var str = this
    var out = [],
        p = 0
    var c = str.charCodeAt(i)
    if (c < 128) {
        out[p++] = c
    } else if (c < 2048) {
        out[p++] = (c >> 6) | 192
        out[p++] = (c & 63) | 128
    } else if ((c & 0xfc00) == 0xd800 && i + 1 < str.length && (str.charCodeAt(i + 1) & 0xfc00) == 0xdc00) {
        // Surrogate Pair
        c = 0x10000 + ((c & 0x03ff) << 10) + (str.charCodeAt(++i) & 0x03ff)
        out[p++] = (c >> 18) | 240
        out[p++] = ((c >> 12) & 63) | 128
        out[p++] = ((c >> 6) & 63) | 128
        out[p++] = (c & 63) | 128
    } else {
        out[p++] = (c >> 12) | 224
        out[p++] = ((c >> 6) & 63) | 128
        out[p++] = (c & 63) | 128
    }
    return out
}

export default FormData

